package apps.sim;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.Map;
import java.util.UUID;

import apps.sim.action.handler.HandlerFactory;
import apps.sim.core.ExecutorFactory;
import apps.sim.core.Trapper;
import apps.sim.core.log.Console;
import apps.sim.core.log.Console.Color;
import apps.sim.core.log.Logger;
import apps.sim.http.HttpBaseHandler;
import apps.sim.http.HttpClassLookupHandler;
import apps.sim.http.HttpDefaultHandler;
import apps.sim.http.HttpHandlerConfLookup;
import apps.sim.http.HttpOperationHandler;
import apps.sim.http.HttpRourceHandler;
import apps.sim.http.HttpSaveHandler;
import apps.sim.http.HttpSettingsHandler;
import apps.sim.http.HttpLoadTestHandler;
import apps.sim.util.Configuration;
import apps.sim.util.InvalidSessionException;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;

public class BaseIndex {

	private static final String CONTEXT_ADMIN = "/simulate/global/admin";
	private static final String CONTEXT_SRC = "/simulate/global/src";
	private static final String CONTEXT_HANDLERS = "/simulate/global/hander";
	private static final String CONTEXT_HANDLER_CONF = "/simulate/global/hander/conf";
	private static final String CONTEXT_EXIT = "/simulate/global/exit";
	private static final String CONTEXT_SAVE = "/simulate/global/save";
	private static final String CONTEXT_DEFAULT = "/simulate/global/default";
	private static final String CONTEXT_LOG_VIEW = "/simulate/global/logview";
	private static final String CONTEXT_STATUS = "/simulate/global/status";
	private static final String CONTEXT_SETTINGS = "/simulate/global/settings";
	// private contexts
	private static final String CONTEXT_EXECUTE_LOAD = "/simulate/private/load";
	private static final String PORT = Configuration.CONFIGURATION.get("shycop.app.port", "20000");
	private static final String HOST = Configuration.CONFIGURATION.get("shycop.url.type", "localhost");

	private String sessionID;
	private static String currentAddress;
	private static String currentURL;
	private HttpServer server;

	public BaseIndex() throws Exception {
		HttpBaseHandler.createSettingsFolder();
		HandlerFactory.getInstance().init();
		String hostAddress = "localhost";// Inet4Address.getLocalHost().getHostAddress();
		try {
			if (HOST.equalsIgnoreCase("NAME")) {
				hostAddress = Inet4Address.getLocalHost().getCanonicalHostName();
			} else if (HOST.equalsIgnoreCase("IP")) {
				hostAddress = Inet4Address.getLocalHost().getHostAddress();
			}
		} catch (Exception e) {
			Console.print("Wrong configuration: shycop.url.type", Color.ERROR);
		}
		final InetSocketAddress inetSocketAddress = new InetSocketAddress(hostAddress, Integer.valueOf(PORT));
		server = HttpServer.create(inetSocketAddress, 0);
		server.createContext(CONTEXT_ADMIN, new HttpOperationHandler());
		server.createContext(CONTEXT_HANDLERS, new HttpClassLookupHandler());
		server.createContext(CONTEXT_HANDLER_CONF, new HttpHandlerConfLookup());
		server.createContext(CONTEXT_SRC, new HttpRourceHandler());
		server.createContext(CONTEXT_EXIT, new HttpExitHandler());
		server.createContext(CONTEXT_SAVE, new HttpSaveHandler());
		server.createContext(CONTEXT_DEFAULT, new HttpDefaultHandler());
		server.createContext(CONTEXT_LOG_VIEW, new HttpLogViewHandler());
		server.createContext(CONTEXT_STATUS, new HttpSessionStatus());
		server.createContext(CONTEXT_SETTINGS, new HttpSettingsHandler());
		server.createContext(CONTEXT_EXECUTE_LOAD, new HttpLoadTestHandler());

		server.setExecutor(ExecutorFactory.arrayBlockingExecutor());
		server.start();
		sessionID = UUID.randomUUID().toString();
		currentURL = "http://" + hostAddress + ":" + PORT + CONTEXT_ADMIN + "?sessionid=" + sessionID;
		currentAddress = "http://" + hostAddress + ":" + PORT;
		HttpSettingsHandler.initState();
	}

	private class HttpLogViewHandler extends HttpBaseHandler {
		public void execute(HttpExchange exchange) throws Exception {
			final Map<String, String> extractParams = extractParams(exchange.getRequestURI());
			final String console = extractParams.get("console");
			if (console != null && console.equalsIgnoreCase("true")) {
				final String sessionid = extractParams.get("sessionid");
				if (sessionid != null && authorize(sessionid)) {
					fetchConsoleData(exchange);
				} else {
					successResponse(exchange, new ByteArrayInputStream("<logs></logs>".getBytes()), "text/xml");
				}
			} else {
				fetchLogs(exchange, extractParams);
			}

		}

		private void fetchConsoleData(HttpExchange exchange) throws IOException {
			final String tail = Console.getLogs();
			successResponse(exchange, new ByteArrayInputStream(tail.getBytes()), "text/xml;charset=utf-8");
		}

		private void fetchLogs(HttpExchange exchange, final Map<String, String> extractParams) throws Exception, IOException {
			final String string = extractParams.get("lines");
			final int lines = (string == null || string.equals("")) ? 200 : Integer.parseInt(string);
			final String tail = Logger.tail(lines);
			successResponse(exchange, new ByteArrayInputStream(tail.getBytes()), "text/plain;charset=utf-8");
		}

	}

	private class HttpExitHandler extends HttpBaseHandler {
		protected void execute(HttpExchange exchange) throws Exception {
			try {
				final String string = extractParams(exchange.getRequestURI()).get("url");
				final String sessionid = extractParams(new URI(string)).get("sessionid");
				if (sessionid.equals("null") || authorize(sessionid)) {
					killApplication();
				}
			} catch (Exception e) {
				killApplication();
			}
		}

	};

	public class HttpSessionStatus extends HttpBaseHandler {
		protected void execute(HttpExchange exchange) throws Exception {
			final String sessionid = extractParams(exchange.getRequestURI()).get("sessionid");
			if (sessionid != null) {
				final boolean isValid = authorize(sessionid);
				if (!isValid) {
					throw new InvalidSessionException("Invalid session");
				}
			}
		}
	}

	private boolean authorize(final String sessionid) {
		return sessionid.equals(getSessionID());
	}

	public String getSessionID() {
		return sessionID;
	}

	public static String getCurrentAddress() {
		return currentAddress;
	}

	public void killApplication() {
		HttpSettingsHandler.proxyServer.closeSocket();
		Trapper.stop();
		server.stop(0);
	}

	public static String getCurrentURL() {
		return currentURL;
	}
}
